/*************************************************************************************
* Copyright (c) 2021 xffish
* c_plus_plus_primer_exercise is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*************************************************************************************/

#include <fstream>
#include <sstream>
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <set>
#include <cctype>
#include <memory>
// use fmt::format(https://github.com/fmtlib/fmt) and should build with -lfmt
#include <fmt/core.h>



using std::cin;
using std::cout;
using std::endl;
using std::map;
using std::set;
using std::string;
using std::vector;

void runQueries(std::ifstream &infile);
class TextQuery;
class QueryResult;
std::ostream &print(const std::ostream &, const QueryResult &);

class QueryResult
{
    friend std::ostream& print(std::ostream&, const QueryResult&);
    public:
        QueryResult(string,std::shared_ptr<set<vector<string>::size_type>>,std::shared_ptr<vector<string>>);
    private:
        string word; //要查询的词
        std::shared_ptr<set<vector<string>::size_type>> lineNumSet;
        std::shared_ptr<vector<string>> lineContentsP;
};

QueryResult::QueryResult(string s,
                        std::shared_ptr<set<vector<string>::size_type>> p,
                        std::shared_ptr<vector<string>> f):
            word(s), lineNumSet(p), lineContentsP(f)
{}

class TextQuery
{
public:
    explicit TextQuery(std::ifstream &);
    QueryResult query(const string&);
private:
    // 每一行的内容
    std::shared_ptr<vector<string>> lineContentsP;
    // 每个单词及其行号的集合
    map<string, std::shared_ptr<set<vector<string>::size_type>>> wordLinesMapping;
};
// 能不能把new改为 make_shared TODO
TextQuery::TextQuery(std::ifstream &infile):lineContentsP(new vector<string>)
{
    cout << "开始创建TextQuery" << endl;
    string tmpLine;
    for (int index=0; std::getline(infile, tmpLine); ++index)
    {
        lineContentsP->push_back(tmpLine);
        //遍历这一行并存入map
        std::istringstream lineStream(tmpLine);
        string word;
        while (lineStream >> word)
        {
            //读到每个单词后存入map，只管去标点不管数字，假设每个单词后最多只有1个标点
            if(std::ispunct(static_cast<unsigned char>(word.back())))
            {
                //有标点要去掉
                word.erase(word.end()-1);
            }
            //干嘛要搞个引用,为了避免shared_ptr拷贝?有这个性能损失考虑?
            auto &tmpSet = wordLinesMapping[word];
            if(!tmpSet)
            {
                //如果是第一次遇到这个单词，就初始化这个std::shared_ptr<set<vector<string>::size_type>>
                // 用=?
                // 用 make_shared?
                tmpSet.reset(new set<vector<string>::size_type>);
            }
            wordLinesMapping[word]->insert(index);
            // std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), "{0:.2f}", 1.23);
            // cout << message << endl;
        }
    }
}

// 根据查询文本输出结果
QueryResult TextQuery::query(const string& queryWord)
{
    const auto iter = wordLinesMapping.find(queryWord);
    if(iter != wordLinesMapping.cend())
    {
        //找到了
        return QueryResult(queryWord, iter->second, lineContentsP);
    }
    else
    {
        //没找到
        // TODO 用new = make_shared
        std::shared_ptr<set<vector<string>::size_type>> nodata(new set<vector<string>::size_type>);
        return QueryResult(queryWord, nodata, lineContentsP);
        
    }
}




void runQueries(std::ifstream &infile)
{
    // infile 是一个 ifstream，指向我们要处理的文件
    TextQuery tq(infile); //保存文件并建立查询 map
    // 与用户交互：提示用户输入要查询的单词，完成查询并打印结果
    while(true)
    {
        cout << "enter word to look for,or q to quit: ";
        string s;
        //若遇到文件尾或用户输入了'q'时循环终止
        if(!(cin >> s) || s == "q")
        {
            break;
        }
        //指向查询并打印结果
        print(cout, tq.query(s)) << endl;
    }

}



std::ostream& print(std::ostream& out, const QueryResult& qr)
{
    out << fmt::format("The word:\"{}\" occurs {} times", qr.word, qr.lineNumSet->size())  << endl;
    for(auto lineNum : *qr.lineNumSet)
    {
        out << fmt::format("\t(line {}) {}\n", lineNum, (*qr.lineContentsP)[lineNum])  << endl;
    }
    return out;
}


int main()
{
    std::ifstream infile("en.txt");
    
    if (infile)
    {
        runQueries(infile);
    }
    else
    {
        cout << "找不到这个文件" << endl;
    }

    return 0;
}